<?php
declare(strict_types=1);

namespace WebApp\core\lib\db\manager;

use ErrorException;
use MongoDB\Driver\Manager;
use MongoDB\Driver\WriteConcern;
use MongoDB\Driver\BulkWrite;
use MongoDB\Driver\Query;
use txz\common\third_lib\model\lib\Dao;
use WebApp\core\lib\Config;

/**
 * Class MongoManager
 * @package WebApp\core\lib
 */
class MongoManager implements Dao
{
    const ERR_ID = 1;
    const ERR_DATA = 2;
    const ERR_DB = 3;
    const HAS_ID = 4;
    const ERR_FILTER = 5;

    private $manager;
    private $connect;
    private $db;
    private $collection;


//    public function __construct($db, $collection, $configName)

    /**
     * MongoManager constructor.
     * @param null $host
     * @param null $port
     * @param null $dbName
     * @param null $user
     * @param null $pwd
     * @param null $logDir
     */
    public function __construct($host = null, $port = null, $dbName = null, $user = null, $pwd = null, $logDir = null)
    {
        $this->db = $db;
        $this->collection = $collection;

        $confArr = Config::getConfArr($configName);
        $this->preConnect($confArr["HOST"], $confArr["PORT"]);
    }

    /**
     * @param $host
     * @param $port
     */
    private function preConnect($host, $port)
    {
        $this->setManager(new Manager("mongodb://{$host}:{$port}"));
        $this->setConnect(new WriteConcern(WriteConcern::MAJORITY, 1000));
    }

    /**
     * @return bool|int
     */
    private function setConfFile()
    {
        $mongoServer = $this->getManager()->getServers()[0];
        if ($mongoServer) {
            $info = $mongoServer->getInfo();
            $primary = explode(':', $info["primary"]);
            $str = '';
            foreach ($info['hosts'] as $v) {
                $str .= "'" . $v . "',";
            }
            $configEOT = <<<EOT
<?php
return [
    "HOST"=>'{$primary[0]}',
    "PORT"=>'{$primary[1]}',
    "DIR"=>__FILE__,




    "RS"=>[{$str}]
];
EOT;
            $confArr = Config::getConfArr('mongodb');
            return file_put_contents($confArr["DIR"], $configEOT);
        } else {
            return false;
        }
    }

    public function __destruct()
    {
//        $confArr = Config::getConfArr('mongodb');
//        if (!hasArr($confArr, 'RS')) {
//            $this->setConfFile();
//        }
    }

    /**
     * @return int
     */
    private function solveDbErr()
    {
        $res = $this->setConfFile();

        if (!$res) {
            $confArr = Config::getConfArr('mongodb');
            $uriArr = $confArr['RS'];
            if ($uriArr && is_array($uriArr) && count($uriArr)) {
                foreach ($uriArr as $uri) {
                    $uri = explode(':', $uri);
                    $this->preConnect($uri[0], $uri[1]);
                    try {
                        $query = new Query(['_id' => 'a']);
                        $this->getManager()->executeQuery($this->db . "." . $this->collection, $query);
                    } catch (ErrorException $e) {
                    }
                    $res = $this->setConfFile();
                    if ($res) {
                        break;
                    }
                }
            }
        }

        return self::ERR_DB;
    }

    /**
     * @param $id
     * @param $arr
     * @return array|int
     */
    public function insertById($id, $arr)
    {
        try {
            if (!$id)
                return self::ERR_ID;
            $arr["_id"] = $id;

            if (!is_array($arr))
                return self::ERR_DATA;

            $bulk = new BulkWrite;
            $bulk->insert($arr);
            $res = $this->getManager()->executeBulkWrite($this->db . "." . $this->collection, $bulk, $this->connect);
            if ($res->getWriteErrors())
                return $this->solveDbErr();
            else
                return [true];
        } catch (ErrorException $e) {

            $res = $this->find(["_id" => $id]);
            if (is_array($res))
                return self::HAS_ID;
            else
                return $this->solveDbErr();
        }
    }


    /**
     * @param $arr
     * @return array|int
     */
    public function insert($arr)
    {
        try {
            if (!is_array($arr))
                return self::ERR_DATA;

            $bulk = new BulkWrite;
            $bulk->insert($arr);
            $res = $this->getManager()->executeBulkWrite($this->db . "." . $this->collection, $bulk, $this->connect);
            if ($res->getWriteErrors())
                return $this->solveDbErr();
            else
                return [true];
        } catch (ErrorException $e) {
            return $this->solveDbErr();
        }
    }

    /**
     * @param $need
     * @return array
     */
    public static function arr2need($need)
    {
        $option = [];
        foreach ($need as $val)
            $option[$val] = 1;
        $option["_id"] = 0;
        $options = [
            'projection' => $option
        ];
        return $options;
    }

    /**
     * @param array $filter
     * @param array $options
     * @return array|int
     */
    public function find($filter = [], $options = [])
    {
        try {
//            if($options)
//                $options = $this->arr2need($options);
            $query = new Query($filter, $options);
            $cursor = $this->getManager()->executeQuery($this->db . "." . $this->collection, $query);
            if ($cursor)
                return $cursor->toArray();
        } catch (ErrorException $e) {
            return $this->solveDbErr();
        }

        return [true];
    }


    /**
     * @param $filter
     * @param int $limit
     * @return array|int
     */
    public function remove($filter, $limit = 1)
    {
        try {
            $bulk = new BulkWrite;
            $bulk->delete($filter, ['limit' => $limit]);
            $res = $this->getManager()->executeBulkWrite($this->db . "." . $this->collection, $bulk, $this->connect);
            if ($res->getWriteErrors())
                return $this->solveDbErr();
            else
                return [true];
        } catch (ErrorException $e) {
            return $this->solveDbErr();
        }
    }

    /**
     * @param array $filter
     * @param $arr
     * @param array $multi_upsert
     * @param string $opt
     * @return array|int
     */
    public function update($filter = [], $arr, $multi_upsert = [false, false], $opt = '$set')
    {
        try {
            if (!is_array($filter) || !count($filter))
                return self::ERR_FILTER;
            $bulk = new BulkWrite;
            $bulk->update(
                $filter,
                [$opt => $arr],
                ['multi' => $multi_upsert[0], 'upsert' => $multi_upsert[1]]
            );
            $res = $this->getManager()->executeBulkWrite($this->db . "." . $this->collection, $bulk, $this->connect);
            if ($res->getWriteErrors())
                return $this->solveDbErr();
            else
                return [true];
        } catch (ErrorException $e) {
            return $this->solveDbErr();
        }
    }

    /**
     * @return Manager
     */
    public function getManager()
    {
        return $this->manager;
    }

    /**
     * @param Manager $manager
     */
    public function setManager($manager)
    {
        $this->manager = $manager;
    }

    /**
     * @return mixed
     */
    public function getConnect()
    {
        return $this->connect;
    }

    /**
     * @param mixed $connect
     */
    public function setConnect($connect)
    {
        $this->connect = $connect;
    }

    public function get_last_sql()
    {
        // TODO: Implement get_last_sql() method.
    }

    /**
     * @param string $tableName
     * @param string $need
     * @param string $where
     * @param string $group
     * @param string $sort
     */
    public function get_obj($tableName = '', $need = '', $where = '', $group = '', $sort = '')
    {
        // TODO: Implement get_obj() method.
    }

    /**
     * @param string $tableName
     * @param string $need
     * @param string $where
     * @param string $group
     * @param string $sort
     * @param string $limit
     */
    public function get_obj_arr($tableName = '', $need = '', $where = '', $group = '', $sort = '', $limit = '')
    {
        // TODO: Implement get_obj_arr() method.
    }

    /**
     * @param $sql
     */
    public function get_obj_arr_by_sql($sql)
    {
        // TODO: Implement get_obj_arr_by_sql() method.
    }

    /**
     * @param $sql
     */
    public function get_obj_by_sql($sql)
    {
        // TODO: Implement get_obj_by_sql() method.
    }

    /**
     * @param string $tableName
     * @param string $data
     */
    public function add_obj($tableName = '', $data = '')
    {
        // TODO: Implement add_obj() method.
    }

    /**
     * @param string $tableName
     * @param string $data
     * @param string $where
     */
    public function update_obj($tableName = '', $data = '', $where = '')
    {
        // TODO: Implement update_obj() method.
    }

    /**
     * @param string $tableName
     * @param string $where
     */
    public function remove_obj($tableName = '', $where = '')
    {
        // TODO: Implement remove_obj() method.
    }

    /**
     * @param $sql
     */
    public function execute($sql)
    {
        // TODO: Implement execute() method.
    }

    /**
     * @return int
     */
    public function get_last_insert_id(): int
    {
        // TODO: Implement get_last_insert_id() method.
    }

    /**
     * @param $call_back
     * @return bool
     */
    public function transaction($call_back): bool
    {
        // TODO: Implement transaction() method.
    }
}